home *** CD-ROM | disk | FTP | other *** search
/ Collection of Tools & Utilities / Collection of Tools and Utilities.iso / archiver / pdtar.zip / LIST.C < prev    next >
C/C++ Source or Header  |  1988-05-15  |  13KB  |  577 lines

  1. /*
  2.  * List a tar archive.
  3.  *
  4.  * Also includes support routines for reading a tar archive.
  5.  *
  6.  * Pubic Domain version written 26 Aug 1985 by John Gilmore (ihnp4!hoptoad!gnu).
  7.  * MS-DOS port 2/87 by Eric Roskos.
  8.  * Minix  port 3/88 by Eric Roskos.
  9.  *
  10.  * @(#)list.c 1.18 9/23/86 Public Domain - gnu
  11.  */
  12. #include <stdio.h>
  13. #include <ctype.h>
  14. #include <sys/types.h>
  15. #include <sys/stat.h>
  16. #ifndef MSDOS
  17. #ifndef V7
  18. #include <sys/file.h>
  19. #endif /* V7 */
  20. #else                            /* ifdef MSDOS */
  21. #include <fcntl.h>
  22. #endif
  23.  
  24. char           *ctime();        /* From libc.a */
  25.  
  26. #define    isodigit(c)    ( ((c) >= '0') && ((c) <= '7') )
  27.  
  28. #include "tar.h"
  29.  
  30. long            from_oct();        /* Decode octal number */
  31. void            demode();        /* Print file mode */
  32.  
  33. union record   *head;            /* Points to current archive header */
  34. struct stat     hstat[1];        /* Stat struct corresponding */
  35. struct stat    *phstat = hstat;        /* to overcome construct ACK C can't handle */
  36.  
  37. void            print_header();
  38. void            skip_file();
  39.  
  40.  
  41. /*
  42.  * Main loop for reading an archive.
  43.  */
  44. void
  45. read_and(do_something)
  46. void            (*do_something) ();
  47.  
  48. {
  49.     int             status = 1;
  50.     int             prev_status;
  51.     char           *xname;
  52.     char           *fixname();
  53.  
  54.     name_gather();                /* Gather all the names */
  55.     open_archive(1);            /* Open for reading */
  56.  
  57.     for (;;)
  58.     {
  59.         prev_status = status;
  60.         status = read_header();
  61.         switch (status)
  62.         {
  63.  
  64.         case 1:                /* Valid header */
  65.             /* We should decode next field (mode) first... */
  66.             /* Ensure incoming names are null terminated. */
  67.             head->header.name[NAMSIZ - 1] = '\0';
  68.             /* make a valid filename for this OS */
  69.             xname = fixname(head->header.name);
  70.  
  71.             if (!name_match(head->header.name))
  72.             {
  73.                 /* Skip past it in the archive */
  74.                 userec(head);
  75.                 /* Skip to the next header on the archive */
  76.                 if (head->header.linkflag != LF_BLK &&
  77.                     head->header.linkflag != LF_CHR)
  78.                     skip_file((long) phstat->st_size);
  79.                 continue;
  80.             }
  81.  
  82.             (*do_something) (xname);
  83.             continue;
  84.  
  85.             /*
  86.              * If the previous header was good, tell them that we are
  87.              * skipping bad ones. 
  88.              */
  89.         case 0:                /* Invalid header */
  90.     case0:
  91.             userec(head);
  92.             if (prev_status == 1)
  93.             {
  94.                 annorec(stderr, tar);
  95.                 fprintf(stderr,
  96.                     "Skipping to next file header...\n");
  97.             }
  98.             continue;
  99.  
  100.         case 2:                /* Block of zeroes */
  101.             if (f_ignorez)
  102.                 goto case0;        /* Just skip if asked */
  103.             /* FALL THRU */
  104.         case EOF:                /* End of archive */
  105.             break;
  106.         }
  107.         break;
  108.     };
  109.  
  110.     close_archive();
  111.     names_notfound();            /* Print names not found */
  112. }
  113.  
  114.  
  115. /*
  116.  * Print a header record, based on tar options.
  117.  */
  118. void
  119. list_archive(xname)
  120. char           *xname;
  121. {
  122.  
  123.     /* Save the record */
  124.     saverec(&head);
  125.  
  126.     /* Print the header record */
  127.     print_header(xname);
  128.  
  129.     /* Skip past it in the archive */
  130.     saverec((union record **) 0);        /* Unsave it */
  131.     userec(head);
  132.  
  133.     /* Skip to the next header on the archive */
  134.     if (head->header.linkflag != LF_BLK &&
  135.         head->header.linkflag != LF_CHR) /* no skip if special file - JER */
  136.         skip_file((long) phstat->st_size);
  137. }
  138.  
  139.  
  140. /*
  141.  * Read a record that's supposed to be a header record.
  142.  * Return its address in "head", and if it is good, the file's
  143.  * size in hstat->st_size.
  144.  *
  145.  * Return 1 for success, 0 if the checksum is bad, EOF on eof,
  146.  * 2 for a block full of zeros (EOF marker).
  147.  *
  148.  * You must always userec(head) to skip past the header which this
  149.  * routine reads.
  150.  */
  151. int
  152. read_header()
  153. {
  154.     register int    i;
  155.     register long   sum, recsum;
  156.     register char  *p;
  157.     register union record *header;
  158.  
  159.     header = findrec();
  160.     head = header;                /* This is our current header */
  161.     if (NULL == header)
  162.         return EOF;
  163.  
  164.     recsum = from_oct(8, header->header.chksum);
  165.  
  166.     sum = 0;
  167.     p = header->charptr;
  168.     for (i = sizeof(*header); --i >= 0;)
  169.     {
  170.  
  171.         /*
  172.          * We can't use unsigned char here because of old compilers, e.g. V7. 
  173.          */
  174.         sum += 0xFF & *p++;
  175.     }
  176.  
  177.     /* Adjust checksum to count the "chksum" field as blanks. */
  178.     for (i = sizeof(header->header.chksum); --i >= 0;)
  179.         sum -= 0xFF & header->header.chksum[i];
  180.     sum += ' ' * sizeof header->header.chksum;
  181.  
  182.     if (sum == recsum)
  183.     {
  184.  
  185.         /*
  186.          * Good record.  Decode file size and return. 
  187.          */
  188.         if (header->header.linkflag == LF_LINK)
  189.             phstat->st_size = 0;    /* Links 0 size on tape */
  190.         else
  191.             phstat->st_size = from_oct(1 + 12, header->header.size);
  192.         return 1;
  193.     }
  194.  
  195.     if (sum == 8 * ' ')
  196.     {
  197.  
  198.         /*
  199.          * This is a zeroed block...whole block is 0's except for the 8
  200.          * blanks we faked for the checksum field. 
  201.          */
  202.         return 2;
  203.     }
  204.  
  205.     return 0;
  206. }
  207.  
  208.  
  209. /* 
  210.  * Decode things from a file header record into a "struct stat".
  211.  * Also set "*stdp" to !=0 or ==0 depending whether header record is "Unix
  212.  * Standard" tar format or regular old tar format.
  213.  *
  214.  * read_header() has already decoded the checksum and length, so we don't.
  215.  *
  216.  * If wantug != 0, we want the uid/group info decoded from Unix Standard
  217.  * tapes (for extraction).  If == 0, we are just printing anyway, so save time.
  218.  */
  219. decode_header(header, st, stdp, wantug)
  220. register union record *header;
  221. register struct stat *st;
  222. int            *stdp;
  223. int             wantug;
  224. {
  225.     st->st_mode = from_oct(8, header->header.mode);
  226.     st->st_mtime = from_oct(1 + 12, header->header.mtime);
  227.  
  228.     if (0 == strcmp(header->header.magic, TMAGIC))
  229.     {
  230.         /* Unix Standard tar archive */
  231.         *stdp = 1;
  232.         if (wantug)
  233.         {
  234. #ifndef MSDOS
  235. #ifndef NONAMES    /* if we have names, still support noname tapes - JER */
  236.             if (header->header.uname[0]) /* JER */
  237.                 st->st_uid = finduid(header->header.uname);
  238.             else
  239. #endif /* NONAMES */                
  240.                 st->st_uid = from_oct(8, header->header.uid);
  241. #ifndef NONAMES
  242.             if (header->header.gname[0]) /* JER */
  243.                 st->st_gid = findgid(header->header.gname);
  244.             else
  245. #endif /* NONAMES */                
  246.                 st->st_gid = from_oct(8, header->header.gid);
  247. #else /* MSDOS */
  248.             st->st_uid = st->st_gid = 0;        /* unsupported in DOS */
  249. #endif /* MSDOS */
  250.         }
  251. #ifndef MSDOS
  252.         switch (header->header.linkflag)
  253.         case LF_BLK:
  254.         case LF_CHR:
  255.             st->st_dev = makedev(from_oct(8, header->header.devmajor),
  256.                 from_oct(8, header->header.devminor));
  257. #endif
  258.     }
  259.     else
  260.     {
  261.         /* Old fashioned tar archive */
  262.         *stdp = 0;
  263.         st->st_uid = from_oct(8, header->header.uid);
  264.         st->st_gid = from_oct(8, header->header.gid);
  265.         st->st_dev = 0;
  266.     }
  267. }
  268.  
  269.  
  270. /*
  271.  * Quick and dirty octal conversion.
  272.  *
  273.  * Result is -1 if the field is invalid (all blank, or nonoctal).
  274.  */
  275. long
  276. from_oct(digs, where)
  277. register int    digs;
  278. register char  *where;
  279. {
  280.     register long   value;
  281.  
  282.     while (isspace(*where))
  283.     {                            /* Skip spaces */
  284.         where++;
  285.         if (--digs <= 0)
  286.             return -1;            /* All blank field */
  287.     }
  288.     value = 0;
  289.     while (digs > 0 && isodigit(*where))
  290.     {                            /* Scan til nonoctal */
  291.         value = (value << 3) | (*where++ - '0');
  292.         --digs;
  293.     }
  294.  
  295.     if (digs > 0 && *where && !isspace(*where))
  296.         return -1;                /* Ended on non-space/nul */
  297.  
  298.     return value;
  299. }
  300.  
  301.  
  302. /*
  303.  * Actually print it.
  304.  */
  305. #define    UGSWIDTH    11            /* min width of User, group, size */
  306. #define    DATEWIDTH    19            /* Last mod date */
  307. static int      ugswidth = UGSWIDTH;    /* Max width encountered so far */
  308.  
  309. void
  310. print_header(xname)
  311. char           *xname;
  312. {
  313.     char            modes[11];
  314.     char           *timestamp;
  315.     char            uform[11], gform[11];        /* These hold formatted ints */
  316.     char           *user, *group;
  317.     char            size[24];    /* Holds a formatted long or maj, min */
  318.     long            longie;        /* To make ctime() call portable */
  319.     int             pad;
  320.     int             header_std;    /* Is header standard or not? */
  321.     register int    i;
  322. #ifdef MSDOS
  323.     char        blanks[26];
  324. #endif
  325.  
  326.     annofile(stdout, (char *) NULL);
  327.  
  328.     if (f_verbose)
  329.     {
  330.         decode_header(head, hstat, &header_std, 0);
  331.  
  332.         /* File type and modes */
  333.         modes[0] = '?';
  334.         switch (head->header.linkflag)
  335.         {
  336.         case LF_NORMAL:
  337.         case LF_OLDNORMAL:
  338.         case LF_LINK:
  339.             modes[0] = '-';
  340.             if ('/' == head->header.name[strlen(head->header.name) - 1])
  341.                 modes[0] = 'd';
  342.             break;
  343.         case LF_DIR:
  344.             mod